// file CoachSynchronization.cpp

// Implementacia synchronizacie kouca

//#include "Coach/CoachAnalyser.h"
#include "Coach/CoachKernel.h"
//#include <math.h>
//#include <stdio.h>
//#include <string.h>

#ifdef WIN32
#include <windows.h> 
#endif

void CoachKernel::OnHear(int time, bool our, int uniformNumber, const char* message)
{
	// TODO: parsovanie sprav od nasich hracov, aby kouc zistil, co zamyslaju
	if(our == true)
	{
	
	}
}

void CoachKernel::OnChangeMode(int time, PlayMode mode)
{
	// Zmena stran
	if(mode == PM_BeforeKickOff)
	{
		// ourSide = ourSide;
		TXTN("---- Hrame %s ----", ourSide ? "vlavo" : "vpravo");
	}
	if((mode == PM_AfterGoal_Our && ourSide) || (mode == PM_AfterGoal_Their && !ourSide))
	{
		scoreOur++;
		TXTN("Skore %d : %d (%d)", scoreOur, scoreTheir, scoreTheir - scoreOur);
	}
	if((mode == PM_AfterGoal_Their && ourSide) || (mode == PM_AfterGoal_Our && !ourSide))
	{
		scoreTheir++;
		TXTN("Skore %d : %d (%d)", scoreOur, scoreTheir, scoreTheir - scoreOur);
	}
	
	if((GetPlayMode() == PM_CornerKick_Our && ourSide) || (GetPlayMode() == PM_CornerKick_Their && !ourSide))
	{
		cornerKicksOur++;
	}
	
	if((GetPlayMode() == PM_CornerKick_Their && ourSide) || (GetPlayMode() == PM_CornerKick_Our && !ourSide)) 
	{
		cornerKicksTheir++;
	}
	
	if((GetPlayMode() == PM_Offside_Our && ourSide) || (GetPlayMode() == PM_Offside_Their && !ourSide))
	{
		offsidesOur++;
	}
	
	if((GetPlayMode() == PM_Offside_Their && ourSide) || (GetPlayMode() == PM_Offside_Our && !ourSide)) 
	{
		offsidesTheir++;
	}
	
	if(mode == PM_TimeOver)
		sayCoachCnt = 0;

	memset(&lastBallInfo, 0, sizeof(lastBallInfo));
	lastKick.who = 0;
	
	if(continousTimeInPlayOn > 0)
		TXTN(" Hra pokracovala bez prerusenia %d cyklov", continousTimeInPlayOn);
	
	continousTimeInPlayOn = 0;
	canSendCLmessage = 0;
	playMode = mode;
}

void CoachKernel::OnChangePlayerType(unsigned number, int type)
{
	if(playerParams && playerTypes)
	{
		// Podporujeme heterogenne typy
		if(number >=1 && number <= 11 && type >=0 && type < playerParams->player_types) {
			playerTypesByNumber[number] = type;
		}
	}
}

void CoachKernel::OnInit(bool side)
{
	commandGen = (CommandGen*) Main::GetInstance()->GetInterface(COMMANDS_TASK);
	commandGen->Eye("on");
//	commandGen->TeamNames(); // quite useless
	ourSide = side;

	TXTN("Pripojeni, %s side, kouc", side ? "vlavo" : "vpravo");

#ifdef WIN32
	SetConsoleTitle("Coach");
#endif

//	Starting();
}

void CoachKernel::OnScore(int time, unsigned int our, unsigned int their)
{
	scoreOur = our;
	scoreTheir = their;
}

void CoachKernel::OnSeeGlobal(const COACH_VISUAL_INFO* vi)
{
	static bool writen = false;
	static int i;
	static Vector ballDistXY;  // Vzdialenost od lopty pre XY
	static float ballDist;     // Vzdialenost od lopty
	static float nearestDist;  // Pre najdenie hraca najblizsieho k lopte
	static int type;           // Typ spracovavaneho heterogenneho hraca
	static int numCurr;        // Pocet hracov, ktori uz boli spracovani
	static Point ballExpPos;   // Ocakavana pozicia lopty (ked nebolo do nej kopnute)
	static float rmax;         // Maximalna variacia pri ocakavanej pozicii lopty
	static float passLength;   // Dlzka nahravky
	static int lenIndex;       // Index podla dlzky nahravky
	static float intersectLeftGoalY;  // y-ova suradnica, kde bude lopta
	static float intersectRightGoalY; // x-ova suradnica, kde bude lopta

/*	
	// Send logo to server if not already sent
	if(teamGraphicOkCount < 256 && teamGraphicTries < TEAM_GRAPHIC_MAX_TRIES)
	{
		DBGN("Sending logo (ok cnt %i)", teamGraphicOkCount);
		if(!SendTeamGraphic(teamGraphicDir))
		{
			WARNN("Error while sending logo (directory: \"%s\")", teamGraphicDir);
			teamGraphicTries = TEAM_GRAPHIC_MAX_TRIES;
		}
	}
*/

	simTime = vi->time;
/*	if((simTime >= 599) && (!writen))
	{
		analyser.printToFile("c:\\test1.txt");
		writen = true;
	}*/

	static int iChangeTypes = 1; 
	int endTime = 4;
	int nTries = 3;

	if(GetPlayMode() == PM_BeforeKickOff)
	{
		if (playerParams != NULL && playerParams->player_types > 0 && nHeteroCounter == playerParams->player_types)
		{
			if (vi->oursCount == 11)
			{
				if (((iChangeTypes % endTime) == 0) && (iChangeTypes <= endTime * nTries))
				{
					MakePlayerSelection();
					if (iChangeTypes == endTime * nTries)
						iChangeTypes = -1;
				}
				if (iChangeTypes > 0)
				{
					iChangeTypes++;
				}
			}
		}
	}
	
	if(simTime == timeOfLastVisualInfo)
		return;

	timeOfLastVisualInfo = simTime;

	if(GetPlayMode() != PM_PlayOn)
	{
		canSendCLmessage = 0;
	}
	// Ak FIFO buffer nie je prazdny, snaz sa poslat spravu
	if(FIFObottom != FIFOtop)
		SayMessage(NULL);

	if(GetPlayMode() != PM_PlayOn)
	{
		// Najdi najblizsieho hraca (pre Kick Off)
		lastNearestPlayer = 0;
		nearestDist = 200; // velke cislo
		
		for(i = 0; i < vi->oursCount; i++)
		{
			numCurr = vi->ours[i].uniformNumber;
			ballDist = (vi->ours[i].pos - vi->ball.pos).GetLength();
			
			if(ballDist < nearestDist)
			{
				lastNearestPlayer = numCurr;
				nearestDist = ballDist;
			}
		}
		
		for(i = 0; i < vi->theirCount; i++)
		{
			numCurr = vi->their[i].uniformNumber;
			ballDist = (vi->their[i].pos - vi->ball.pos).GetLength();
			
			if(ballDist < nearestDist)
			{
				lastNearestPlayer = numCurr + 11;
				nearestDist = ballDist;
			}
		}

		return;
	}

	if(GetPlayMode() == PM_PlayOn) 
	{
		timeInPlayOn++;
		continousTimeInPlayOn++;

		if(canSendCLmessage > 0)
			canSendCLmessage--;

		analyser.Update(vi);
	}

//	char msg[8196];
	
	// Kazdych n cyklov vyhodnotime analyzu
/*	if ((simTime % 300 == 0) && (simTime > 3))
	{
		unsigned int attack, defence;
		char formationassembly[20] = {0};
//		char counts[4];
		
		int numatk, numdef, nummid, offset;
		strcpy(msg, "(define (definerule ChangeFormation direc ((true) Formation");

		// urcia sa pocty hracov v subformaciach
		attack = analyser.CountAttackFitness(&formations);
		numatk = formations.GetPlayersCountInUnit(attack);
//		attack = 16; numatk = 4;
		defence = analyser.CountDefenceFitness(&formations);
		offset = defence%100;
		defence -= offset;
		defence /= 100;
		numdef = formations.GetPlayersCountInUnit(defence);

		nummid = 10-numatk-numdef;
		switch(nummid)
		{
			// TODO prerobit mid podla toho, ktore subformacie budu linie o n hracoch(2-5)
		case 1:
			sprintf(formationassembly,"%d%d%d%c%c%c%c)))", numdef, nummid, numatk,
				defence + 'A', 'A', attack + 'A', offset + 'A');
			strcat(msg, formationassembly);
			TXT("Formation change: %s", msg);
			SayMessage(msg);
			break;
		case 2:
			sprintf(formationassembly,"%d%d%d%c%c%c%c)))", numdef, nummid, numatk,
				defence + 'A', 'B', attack + 'A', offset + 'A');
			strcat(msg, formationassembly);
			TXT("Formation change: %s", msg);
			SayMessage(msg);
			break;
		case 3:
			sprintf(formationassembly,"%d%d%d%c%c%c%c)))", numdef, nummid, numatk,
				defence + 'A', 'H', attack + 'A', offset + 'A');
			strcat(msg, formationassembly);
			TXT("Formation change: %s", msg);
			SayMessage(msg);
			break;
		case 4:
			sprintf(formationassembly,"%d%d%d%c%c%c%c)))", numdef, nummid, numatk,
				defence + 'A', 'O', attack + 'A', offset + 'A');
			strcat(msg, formationassembly);
			TXT("Formation change: %s", msg);
			SayMessage(msg);
			break;
/*		case 5:
			sprintf(formationassembly,"%d%d%d%c%c%c%c)))", numdef, nummid, numatk,
				defence + 'A', 'U', attack + 'A', offset + 'A');
			strcat(msg, formationassembly);
			SayMessage(msg);
			break;
		case 6:
			sprintf(formationassembly,"%d%d%d%c%c%c%c)))", numdef, nummid, numatk,
				defence + 'A', 'Z', attack + 'A', offset + 'A');
			strcat(msg, formationassembly);
			SayMessage(msg);
			break;*/
/*		default:
			TXT("Invalid Formation\n\n");
			break;
		}
		analyser.Reset();
	}
*/
//  Vypneme menenie formacie na zaklade inkasovanych golov
/*	if(scoreTheir - scoreOur > 5)
	{
		if(advice != 3)
		{
			advice = 3;
			strcpy(msg, "(define (definerule ChangeFormation direc ((true) Formation622AAA)))");
			SayMessage(msg);
		}
	}
	else if(scoreTheir - scoreOur > 3)
	{
		if(advice != 2)
		{
			advice = 2;
			strcpy(msg, "(define (definerule ChangeFormation direc ((true) Formation523AAB)))");
			SayMessage(msg);
		}
	}
	else if(scoreTheir - scoreOur > 1)
	{
		if(advice != 1)
		{
			advice = 1;
			strcpy(msg, "(define (definerule ChangeFormation direc ((true) Formation433AAB)))");
			SayMessage(msg);
		}
	}


	if(timeInPlayOn == 900 && scoreTheir - scoreOur <= 1) // send change formation
	{
		// Lopta bola v strednom pasme viac ako 60% herneho casu
		if(statsBall.cyclesInZone[2] / (float)timeInPlayOn > .6f)
		{
			strcpy(msg, "(define (definerule ChangeFormation direc ((true) Formation343BAB)))");
			SayMessage(msg);
		}
	}
/*
	if(timeInPlayOn % 900 == 899) // send change formation
	{
		strcpy(msg, "(define (definerule ChangeFormation direc ((true) Formation");
		switch(GetFormation(false))
		{
			case FORMATION622:
				strcat(msg, "334AAA");
			break;
			case FORMATION532:
				strcat(msg, "334AAA");
			break;
			case FORMATION433:
				strcat(msg, "433AAB");
			break;
			case FORMATION523:
				strcat(msg, "343AAB");
			break;
			case FORMATION343:
				strcat(msg, "343AAB");
			break;
			case FORMATION334:
				strcat(msg, "532ABB");
			break;
		}
		strcat(msg, ")))");
//		SayMessage(msg);
	}
*/

	// Detekcia kopnutia
	ballExpPos = lastBallInfo.pos + lastBallInfo.speed;
	rmax = serverParams->ball_rand * lastBallInfo.speed.GetLength();

	// find out, if the expected position variation exceeded rmax -> it's a kick

	// Zisti, ci variancia ocakavanej pozicie prekracuje rmax -> ide o kopnutie 
	if((fabs(vi->ball.speed.GetX()) > 0.001 || fabs(vi->ball.speed.GetY()) > 0.001) &&
	   (fabs(ballExpPos.GetX() - vi->ball.pos.GetX()) > rmax ||
	    fabs(ballExpPos.GetY() - vi->ball.pos.GetY()) > rmax)) 
	{
		TXT("KICK in %d by %d", simTime - 1, lastNearestPlayer);
		if(lastKick.who > 0)
		{
			passLength = (lastKick.from - lastBallInfo.pos).GetLength();
			lenIndex = GetLengthIndex(passLength);

			if(lastNearestPlayer < 12 && lastKick.who < 12 || lastNearestPlayer > 11 && lastKick.who > 11)
			{
				if(lastNearestPlayer == lastKick.who)
					TXTN(" Driblovanie"); 
				else 
				{
					TXTN(" Uspesna prihravka %fm",
						(lastKick.from - lastBallInfo.pos).GetLength());

					if(lastNearestPlayer < 12)
					{
						statsOurPlayer[lastNearestPlayer].passesReceived++;
						statsOurPlayer[lastKick.who].passesAll[lenIndex]++;
						statsOurPlayer[lastKick.who].passesSuccess[lenIndex]++;
						statsOurPlayer[lastKick.who].sumPassSucLen += passLength;
					}
					else 
					{
						statsTheirPlayer[lastNearestPlayer - 11].passesReceived++;
						statsTheirPlayer[lastKick.who - 11].passesAll[lenIndex]++;
						statsTheirPlayer[lastKick.who - 11].passesSuccess[lenIndex]++;
						statsTheirPlayer[lastKick.who - 11].sumPassSucLen += passLength;
					}
				}
			}
			else
			{
				TXTN(" Prerusena nahravka");
				if(lastNearestPlayer < 12)
				{
					statsOurPlayer[lastNearestPlayer].interceptions++;
					statsTheirPlayer[lastKick.who - 11].passesAll[lenIndex]++;
				}
				else
				{
					statsTheirPlayer[lastNearestPlayer - 11].interceptions++;
					statsOurPlayer[lastKick.who].passesAll[lenIndex]++;
				}
			}
		}
		else TXTN("");

		// Zisti, ci ide o kopnutie na branu
		static float t;
		static float len, spdl;
		t = (flagPos[GOAL_L].GetX() - vi->ball.pos.GetX()) / vi->ball.speed.GetX();
		intersectLeftGoalY = vi->ball.pos.GetY() + vi->ball.speed.GetY() * t;
		
		if(t > 0 && intersectLeftGoalY > -10 && intersectLeftGoalY < 10)
		{
			len = (vi->ball.pos - Point(flagPos[GOAL_L].GetX(), intersectLeftGoalY)).GetLength();
			spdl = vi->ball.speed.GetLength() * serverParams->ball_decay;
			while(len > 0 && spdl > SPEED_ON_GOAL_LINE_MIN) {
				len -= spdl;
				spdl *= serverParams->ball_decay;
			}
			if(len <= 0)
			{
				len = (vi->ball.pos - Point(flagPos[GOAL_L].GetX(), intersectLeftGoalY)).GetLength();
		
				// Ak je to nas hrac a nasa strana je vpravo
				if(lastNearestPlayer < 12 && ourSide == false)
				{
					statsOurPlayer[lastNearestPlayer].shootsOnGoal++;
					statsOurPlayer[lastNearestPlayer].sumLenShootsOnGoal += len;
					TXTN(" - on left goal from %.2fm (exp. speed on goal line = %.2f)", len, spdl);
				}
				
				// Ak je to superov hrac a nasa strana je vlavo
				if(lastNearestPlayer > 11 && ourSide == true)
				{
					statsTheirPlayer[lastNearestPlayer - 11].shootsOnGoal++;
					statsTheirPlayer[lastNearestPlayer - 11].sumLenShootsOnGoal += len;
					TXTN(" - on left goal from %.2fm (exp. speed on goal line = %.2f)", len, spdl);
				}
			}
		}

		t = (flagPos[GOAL_R].GetX() - vi->ball.pos.GetX()) / vi->ball.speed.GetX();
		intersectRightGoalY = vi->ball.pos.GetY() + vi->ball.speed.GetY() * t;
		
		if(t > 0 && intersectRightGoalY > -10 && intersectRightGoalY < 10)
		{
			len = (vi->ball.pos - Point(flagPos[GOAL_R].GetX(), intersectRightGoalY)).GetLength();
			spdl = vi->ball.speed.GetLength();
			
			while(len > 0 && spdl > SPEED_ON_GOAL_LINE_MIN)
			{
				len -= spdl;
				spdl *= serverParams->ball_decay;
			}
			
			if(len <= 0)
			{
				len = (vi->ball.pos - Point(flagPos[GOAL_R].GetX(), intersectRightGoalY)).GetLength();
				
				// Ak je to nas hrac a nasa strana je vlavo				
				if(lastNearestPlayer < 12 && ourSide == true)
				{
					statsOurPlayer[lastNearestPlayer].shootsOnGoal++;
					statsOurPlayer[lastNearestPlayer].sumLenShootsOnGoal += len;
					TXTN(" - on right goal from %.2fm (exp. speed on goal line = %.2f)", len, spdl);
				}
				// Ak je to superov hrac a nasa strana je vpravo
				if(lastNearestPlayer > 11 && ourSide == false)
				{
					statsTheirPlayer[lastNearestPlayer - 11].shootsOnGoal++;
					statsTheirPlayer[lastNearestPlayer - 11].sumLenShootsOnGoal += len;
					TXTN(" - on right goal from %.2fm (exp. speed on goal line = %.2f)", len, spdl);
				}
			}
		}

		lastKick.from = lastBallInfo.pos;
		lastKick.when = simTime - 1;
		lastKick.who = lastNearestPlayer;
	}
	lastBallInfo = vi->ball;


	// Statistika pre loptu
	if(vi->ball.pos.GetX() < 0) // lopta je na lavej strane
	{
		if(ourSide == true)
			statsBall.cyclesOnOurHalf++;
		else
			statsBall.cyclesOnTheirHalf++;
	} 
  else // lopta je na pravej strane
	{
		if(ourSide == false)
			statsBall.cyclesOnOurHalf++;
		else
			statsBall.cyclesOnTheirHalf++;
	}
  for(i = 0; i < NUM_ZONES; i++)
  {
    if(zones[i].from >= vi->ball.pos.GetX() && vi->ball.pos.GetX() <= zones[i].to)
		statsBall.cyclesInZone[i]++;
  }
	
	lastNearestPlayer = 0;
	nearestDist = 200; // velke cislo

	// Debugova statistika pre nasich hracov
	for(i = 0; i < vi->oursCount; i++)
	{
		numCurr = vi->ours[i].uniformNumber;
		type = GetTypeByNumber(numCurr);
		ballDistXY.SetX((float) fabs(vi->ours[i].pos.GetX() - vi->ball.pos.GetX()));
		ballDistXY.SetY((float) fabs(vi->ours[i].pos.GetY() - vi->ball.pos.GetY()));
		ballDist = (vi->ours[i].pos - vi->ball.pos).GetLength();

		if(ballDist < BALL_NEAR_DIST)
		{
			statsOurPlayer[numCurr].cyclesNearBall++;
			statsOurPlayer[numCurr].sumSpeedNearBall += 
				vi->ours[i].speed.GetLength();
		}

		// Urci jeho rolu (D, M alebo A) podla jeho vzdialenosti k lopte a na ktorej je to strane
		if(ballDist < DIST_X_MID_FIELDER)
		{
			statsOurPlayer[numCurr].cyclesMidfielder++;
		}
		else  
		{
			// Hrac je medzi loptou a pravou brankou
			if(vi->ball.pos.GetX() < vi->ours[i].pos.GetX())
			{ 
				// Sme na lavej strane
				if(ourSide == true)
					statsOurPlayer[numCurr].cyclesAttacker++;
				else 
					statsOurPlayer[numCurr].cyclesDefender++;
			}
			else // Hrac je medzi loptou a lavou brankou
			{
				// Sme na lavej strane
				if(ourSide == true)
					statsOurPlayer[numCurr].cyclesDefender++;
				else
					statsOurPlayer[numCurr].cyclesAttacker++;
			}
		}

		statsOurPlayer[numCurr].sumDistFromBall += ballDistXY;

		// Do lopty sa da kopnut
		if(serverParams->ball_size + playerTypes[type].player_size + playerTypes[type].kickable_margin > ballDist)
		{
			statsOurPlayer[numCurr].cyclesWithBall++;
		}

		// Najdi najblizsieho hraca k lopte
		if(ballDist < nearestDist)
		{
			lastNearestPlayer = numCurr;
			nearestDist = ballDist;
		}
	}

	// Debugova statistika pre superovych hracov
	for(i = 0; i < vi->theirCount; i++)
	{
		numCurr = vi->their[i].uniformNumber;
		type = GetTypeByNumber(numCurr);
		ballDistXY.SetX((float) fabs(vi->their[i].pos.GetX() - vi->ball.pos.GetX()));
		ballDistXY.SetY((float) fabs(vi->their[i].pos.GetY() - vi->ball.pos.GetY()));
		ballDist = (vi->their[i].pos - vi->ball.pos).GetLength();

		if(ballDist < BALL_NEAR_DIST)
		{
			statsTheirPlayer[numCurr].cyclesNearBall++;
			statsTheirPlayer[numCurr].sumSpeedNearBall += 
				vi->their[i].speed.GetLength();
		}
		// Urci jeho rolu (D, M alebo A) podla jeho vzdialenosti k lopte a na ktorej je to strane
		if(ballDist < DIST_X_MID_FIELDER)
		{
			statsTheirPlayer[numCurr].cyclesMidfielder++;
		}
		else  
		{
			// Hrac je medzi loptou a pravou brankou
			if(vi->ball.pos.GetX() < vi->their[i].pos.GetX())
			{ 
				// Sme na pravej strane
				if(ourSide != true)
					statsTheirPlayer[numCurr].cyclesAttacker++;
				else
					statsTheirPlayer[numCurr].cyclesDefender++;
			}
			else // Hrac je medzi loptou a lavou brankou
			{
				// Sme na pravej strane
				if(ourSide != true)
					statsTheirPlayer[numCurr].cyclesDefender++;
				else
					statsTheirPlayer[numCurr].cyclesAttacker++;
			}
		}

		statsTheirPlayer[numCurr].sumDistFromBall += ballDistXY;

		// Do lopty sa da kopnut
		if(serverParams->ball_size + playerTypes[type].player_size + playerTypes[type].kickable_margin > ballDist)
		{
			statsTheirPlayer[numCurr].cyclesWithBall++;
		}

		// Najdi najblizsieho hraca k lopte
		if(ballDist < nearestDist)
		{
			lastNearestPlayer = numCurr + 11;
			nearestDist = ballDist;
		}
	}
}
